home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 44 / Amiga Format CD44 (1999-08-26)(Future Publishing)(GB)(Track 1 of 3)[!][issue 1999-10].iso / -in_the_mag- / basics / amos / optim_tips.lha / AMOS_Optimisations.txt
Text File  |  1997-04-09  |  21KB  |  661 lines

  1.  
  2.                     >>  AMOS Code Optimisation Tips  <<
  3.                        -----------------------------
  4.  
  5. Aminet - dev/amos/Optim_Tips.lha
  6.  
  7. Compiled by:
  8.  
  9.    Ben Wyatt       bwyatt@paston.co.uk
  10.  
  11. Contributors:
  12.  
  13.    Garfield Benjamin  gbenjam@ix.netcom.com
  14.    Ben Wyatt          bwyatt@paston.co.uk
  15.    Paul Hickman       ph@doc.ic.ac.uk
  16.    Tim Lewis          T.Lewis@bton.ac.uk
  17.    Petri Hakkinen     mystic@tlti.tokem.fi
  18.    Mark de Jong       JP.de.Jong@inter.nl.net
  19.    Paul Heald         u5o83@cc.keele.ac.uk
  20.  
  21. Here are a few optimisation tips for those of you trying to squeeze that
  22. little bit more speed out of code. With the follow examples, the speed
  23. increase may not be noticeable, but they will all make it that little bit
  24. faster.
  25.  
  26. Important: Do not go through your entire AMOS program collection, converting
  27. all your code so it's slightly faster. Only alter code which you think is
  28. /too/ slow. For example, don't change your 3D renderer just to cut down the
  29. rendering time 1/50 of a second or something silly. And similarly, don't
  30. bother fiddling with the main loop of a game when it already runs in a Vbl!
  31. A lot of these optimisation tips produce some messy code so often it isn't
  32. worth changing things!
  33.  
  34. Note: Some of these speed increases only work when compiled, mainly with
  35. the AMOSPRO Compiler. A lot of them are only tested this way. It must also
  36. be stated that these tips may be incorrect for some CPUs - they have mainly
  37. been tested on an 68020, but caches and different instruction cycle timings
  38. could alter the results. Some sort of chart may be available in future
  39. releases of this document - the best idea is to experiment on your system.
  40.  
  41. Each tip is marked by a *, followed by the amount of speed increase, shown
  42. by a S, M, L or V. These stand for Small, Medium, Large and Varies.
  43.  
  44. ------------------------------------------------------------------------------
  45.  
  46.                             >>  COMPARISONS  <<
  47.                                -------------
  48.  
  49. *S Don't use Sgn(n), use n<0 and n>0 instead.
  50.  
  51.    Eg.
  52.       Don't use:
  53.          If Sgn(n)=-1 Then blah blah
  54.          If Sgn(n)=1 Then blah de blah
  55.  
  56.       Use this instead:
  57.          If n<0 Then blah blah
  58.          If n>0 Then blah de blah
  59.  
  60. *M Use Abs(n)<v instead of n>-v and n<v.
  61.  
  62.    Eg.
  63.       Don't use:
  64.          If n>-5 and n<5 Then blah
  65.  
  66.       Use this instead:
  67.          If Abs(n)<5 Then blah
  68.  
  69. *S Use < and > instead of <= and >= whenever possible. They are slightly
  70.    faster. Strange, but true.
  71.  
  72.    Eg. When comparing immediate-values
  73.       Don't use:
  74.          If n<=10 Then blah
  75.  
  76.       Use this instead:
  77.          If n<11 Then blah
  78.  
  79.    But, when using variables, don't use this kind of thing:
  80.       A=10
  81.       If B>A-1 Then etc
  82.    instead of:
  83.       A=10
  84.       If B>=A Then etc
  85.  
  86. *M Don't do stuff like If n=True, If n=False, or If n<>0.
  87.  
  88.    Eg.
  89.       Don't use:
  90.          If a=True or b=False or c<>0 Then blah blah
  91.  
  92.       Use this instead:
  93.          If a or Not(b) or c Then blah blah
  94.  
  95.    But ONLY do this if you are sure b is a boolean value. "Not" simply
  96.    bitflips an integer so Not -1 is 0, but Not 2 is $FFFFFFFE.
  97.  
  98.    AMOSPro is rather buggy with the Not() command, so you may have to
  99.    experiment a bit.
  100.  
  101.    Also, using =False instead of =0 is faster. Another trick you should be
  102.    aware of is that you can use the optimised version of <>0 where you
  103.    would normally use >0 (or <0) if the routine only returns values >=0.
  104.  
  105.    Eg.
  106.       Don't use:
  107.          If Instr(a$,"Something")>0 Then blah
  108.  
  109.       Do this instead:
  110.          If Instr(a$,"Something") Then blah
  111.  
  112. ------------------------------------------------------------------------------
  113.  
  114.                            >>  ARRAY ACCESS  <<
  115.                               --------------
  116.  
  117. *M For your most used array of 8 (or less) elements, use Dreg() instead of
  118.    it (it can still hold the same range of values). Note: The Dreg() array
  119.    isn't actually using the data registers; it's an AMOS internal data
  120.    structure. However, it's still faster than a standard array.
  121.  
  122.    Eg.
  123.       Don't use:
  124.          Dim n(7)
  125.          For a=0 to 7:Print n(a):Next a
  126.  
  127.       Use this instead:
  128.          For a=0 To 7:Print Dreg(a):Next a
  129.  
  130. *S Use single dimensional arrays instead of two dimensional ones if
  131.    appropriate.
  132.  
  133.     Eg.
  134.        Don't use:
  135.           Dim A(2,100)
  136.  
  137.        Use this instead:
  138.           Dim A0(100),A1(100),A2(100)
  139.  
  140. ------------------------------------------------------------------------------
  141.  
  142.                           >>  MATH OPERATIONS  <<
  143.                              -----------------
  144.  
  145. *L Whenever possible use powers of 2 when multiplying or dividing. The
  146.    AMOSPro Compiler will automatically optimise these to lsl and lsr.
  147.    DON'T use the lsl or lsr commands in extensions, as they are over
  148.    TWICE as slow!
  149.  
  150.    Eg.
  151.       Don't use:
  152.          n=a*30
  153.  
  154.       Use this instead (if suitable):
  155.          n=a*32
  156.  
  157.    As well as this, you can also do:
  158.       n=x*160 -> n=x*(128+32) -> n=x*128+x*32 (tadaa)
  159.       Also n=x*192 -> n=x*128+x*64 etc.
  160.  
  161.    But this kind of thing is not faster:
  162.       a=32 : b=7 : c=a*b
  163.  
  164. *L Never use floating points. Use integers multiplied by a power of 2. You
  165.    can actually decide what sort of precision you want in your decimal places
  166.    and then multiply them out. For example, if you decide on about 2 point
  167.    accuracy, you can multiply all your values by 128 (2^7, close enough to
  168.    100) and then do the calculations. When you have the results simply divide
  169.    by 128 to get the required result.
  170.  
  171.    Eg.
  172.       Don't use:
  173.          x#=x#*1.5
  174.          Plot x#,100
  175.  
  176.       Use this instead:
  177.          x=x*(3*128) : Rem Same as x=x*(1.5*256)
  178.                        Rem 3*128 is calculated at compilation time
  179.          Plot X/256,100
  180.  
  181. *L Predefine as much as possible. Especially useful for Sin and Cos etc.
  182.  
  183.    Eg.
  184.       Don't use:
  185.          Repeat
  186.             If Sqr(x*x+y*y)=10 Then blah blah blah
  187.          Until Something
  188.  
  189.       Use this instead:
  190.          ' This code should be placed at the beginning of your code.
  191.          xmax=Maximum value of x : ymax=Maximum value of y
  192.          Dim QUICKSQR(xmax,ymax)
  193.          For x=0 to xmax
  194.             For y=0 to ymax
  195.                QUICKSQR(x,y)=SQR(x*x+y*y)
  196.             Next y
  197.          Next x
  198.          ' [Other code]
  199.          Repeat
  200.             If QUICKSQR(Abs(x),Abs(y))=10 Then blah blah blah
  201.          Until Something
  202.  
  203. *S Use N=N+A instead of using Inc, Dec or Add. Although the manual claims
  204.    they are faster, when using the AMOSPro Compiler, they actually turn
  205.    out slightly slower.
  206.  
  207.    Eg.
  208.       Don't use:
  209.          Inc a : Dec b : Add c,10
  210.       Use this instead:
  211.          a=a+1 : b=b-1 : c=c+10
  212.  
  213.    Note: Although Add is slower in it's short form, the full version
  214.    with the base To top part is faster than the equivilant code.
  215.  
  216.    This only applies to standard variables - DON'T use this with arrays!
  217.  
  218. ------------------------------------------------------------------------------
  219.  
  220.                            >>  MISCELLANEOUS  <<
  221.                               ---------------
  222.  
  223. *L Get yourself a fast extension, such as AMCAF or Turbo Plus. This will
  224.    only speed things up where an extension command replaces an internal
  225.    command with a faster one - Easylife and AMCAF will do this for many
  226.    string operations and, AMCAF, Turbo and Powerbobs do it for graphics.
  227.  
  228.    Eg.
  229.       Don't use:
  230.          Plot x,y,c
  231.  
  232.       Use this instead:
  233.          Turbo Plot x,y,c (for AMCAF)
  234.       or F Plot x,y,c (for Turbo Plus)
  235.  
  236.    Many extensions can be found on Aminet (FTP from wuarchive.wustl.edu
  237.    in systems/amiga/aminet/dev/amos).
  238.  
  239. *V Use assembler procedures for inner loops. Beware though, it does take
  240.    a small amount of CPU time to jump to an assembler procedure, so with
  241.    small routines, it's simply not worth it.
  242.  
  243. *S Use "Copper Off" to disable the screen while doing intense communication.
  244.    The copper then stops stealing clock cycles from the processor (Unlike
  245.    Multi No and such instructions, this really does work). Use "Copper On"
  246.    to re-enable the normal system.
  247.  
  248. *L Use Fill to clear an array to a certain value, rather than the
  249.    equivilant For / Next loop.
  250.  
  251.    Eg.
  252.       Don't use:
  253.          For n=0 to 1000
  254.             A(n)=27
  255.          Next n
  256.  
  257.       Use this instead:
  258.          Fill Varptr(A(0)) To Varptr(A(1000)),27
  259.  
  260. *S Use global variables to return parameters from procedures, rather than
  261.    Param.
  262.  
  263.    Eg.
  264.       Don't use:
  265.          _HELLO[10,20] : pram=Param : Print pram
  266.          Procedure _HELLO[x,y]
  267.             temp=0
  268.             For n=x To y
  269.                If a(n)=0 Then temp=n : Exit
  270.             Next n
  271.          End Proc[temp]
  272.  
  273.       Use this instead:
  274.          Global pram
  275.          _HELLO[10,20] : Print pram
  276.          Procedure _HELLO[x,y]
  277.             pram=0
  278.             For n=x To y
  279.                If a(n)=0 Then pram=n : Pop Proc
  280.             Next N
  281.          End Proc
  282.  
  283.    However, don't do things like this, which are actually slower.
  284.  
  285.    Don't do:
  286.       _HELLO[10,20] : Print A
  287.       Procedure _HELLO[X,Y]
  288.          Shared A
  289.          A=X*Y
  290.       End Proc
  291.  
  292.    Do this:
  293.       _HELLO[10,20] : Print Param
  294.       Procedure _HELLO[X,Y]
  295.       End Proc[X*Y]
  296.  
  297. *L Use subroutines instead of procedures. Although they're messier, they are
  298.    several times faster! Of course, with this method, you can not pass
  299.    parameters, but all the variables you were using will still be accessible.
  300.  
  301.    Eg.
  302.       Don't use:
  303.          _SOMETHING
  304.          Procedure _SOMETHING
  305.             Code
  306.          End Proc
  307.  
  308.       Use this instead:
  309.          Gosub _SOMETHING
  310.          _SOMETHING:
  311.             Code
  312.          Return
  313.  
  314. ------------------------------------------------------------------------------
  315.  
  316.                      >>  TRADITIONAL OPTIMISATIONS  <<
  317.                         ---------------------------
  318.  
  319. *V Move invariant statement outside loops (Standard optimising compiler
  320.    stuff).
  321.  
  322.    Eg.
  323.       If you have something like this:
  324.          Repeat
  325.             If x<a*20 Then do something groovy
  326.          Until something
  327.  
  328.       If the rest of the loop does not modify a, this can become:
  329.          temp=a*20
  330.          Repeat
  331.             If x<temp Then do something groovy
  332.          Until something
  333.  
  334. *V Unroll your Loops!! Cuts down on Stack-Access eliminating Loop-overhead
  335.    and replacing it with dedicated code. Don't go TOO overboard though.
  336.    Normally five iterations is enough to gain a bit of speed. Too many
  337.    iterations unrolled will actually lose speed on systems with CPU caches.
  338.  
  339.    Eg.
  340.       Don't use:
  341.          For X=1 To 20
  342.             UPDATE_ALIEN[X]
  343.          Next X
  344.  
  345.        Use this instead:
  346.          For X=1 To 16 Step 5
  347.             UPDATE_ALIEN[X]
  348.             UPDATE_ALIEN[X+1]
  349.             UPDATE_ALIEN[X+2]
  350.             UPDATE_ALIEN[X+3]
  351.             UPDATE_ALIEN[X+4]
  352.          Next X
  353.  
  354.    Note: You should only carry out this optimisation on small segments of
  355.    code, such as plotting pixels, otherwise your code can get very bloated.
  356.  
  357. *M Eliminate stack access (the reason Procedures are slower than Gosubs)
  358.  
  359.    Eg.
  360.       Don't use:
  361.          For X=1 To 20
  362.             Gosub MOVE_ALIEN : Rem This uses the stack 40 times
  363.          Next X
  364.          ' Other code
  365.          MOVE_ALIEN:
  366.          Moves Alien X
  367.          Return
  368.  
  369.       Use this instead:
  370.          Gosub MOVE_ALIENS : Rem This only uses the stack twice.
  371.          ' Other code
  372.          MOVE_ALIENS:
  373.          For X=1 To 20
  374.             Move Alien X
  375.          Next X
  376.          Return
  377.  
  378.    Also, try to move local variables out of procedures to make them global.
  379.    They are significantly faster to use.
  380.  
  381. ------------------------------------------------------------------------------
  382.  
  383.                              >>  GRAPHICS  <<
  384.                                 ----------
  385.  
  386. *M Use Cls col,x1,y1 To x2,y2 instead of Bar x1,y1 To x2,y2.
  387.    Note: Use R Bar in Turbo instead if possible.
  388.  
  389. *S Use Polyline to draw boxes instead of Box.
  390.    Note: Use R Box in Turbo instead if possible.
  391.  
  392.    Eg.
  393.       Don't use:
  394.          Box 10,10 To 10,10
  395.  
  396.       Use this instead:
  397.          Polyline 10,10 To 20,10 To 20,20 To 10,20
  398.  
  399. ------------------------------------------------------------------------------
  400.  
  401.                               >>  STRINGS  <<
  402.                                  ---------
  403.  
  404. *L Use Peek(Varptr(a$)+x-1) instead of Asc(Mid$(a$,x,1)) and
  405.    Poke Varptr(a$)+x-1,Asc(b$) instead of Mid$(a$,x,1)=b$.
  406.  
  407.    Eg.
  408.       Don't use:
  409.          A=Asc(Mid$(a$,3,1))
  410.          Mid$(a$,6,1)=b$
  411.  
  412.       Use this instead:
  413.          A=Peek(Varptr(a$)+2)
  414.          Poke Varptr(a$)+5,Asc(b$) : Rem Asc(b$) could be predefined
  415.  
  416.    Note: This is just under three times as fast!
  417.  
  418. *M This routine is for making the string 1 character shorter. It may be
  419.    helpful in an program to replace the input command (if you'd ever need
  420.    to optimise such a routine which is unlikely).
  421.  
  422.    Eg.
  423.       Don't use:
  424.          S$=Space$(1000)
  425.          For X=1 To 1000
  426.             S$=Left$(S$,Len(S$)-1)
  427.          Next X
  428.  
  429.       Use this instead:
  430.          S$=Space$(1000)
  431.          POS=Varptr(S$) : Rem Put this statement inside the loop if you
  432.                           Rem do any standard string operations.
  433.          For X=1 To 1000
  434.             Doke POS-2,Deek(POS)-1
  435.          Next X
  436.  
  437.    Note: This routine could be rather risky, as it is done outside the AMOS
  438.    string handling system.
  439.  
  440. *S Don't use Chr$(code) in routines but replace them with a string.
  441.  
  442.    Eg.
  443.       Don't use:
  444.          I=Instr(ST$,Chr$(0))
  445.  
  446.       Use this instead:
  447.          C0$=Chr$(0)      : Rem At the beginning of your prog
  448.          I=Instr(ST$,C0$) : Rem In the routine
  449.  
  450. *V A quasi-optimisation: Use "n=Free" at points where you don't care about
  451.    the speed, to reduce the chances of the AMOS string garbage collector
  452.    being called when you do care about the speed. This will only have an
  453.    effect when strings are used in the routine.
  454.  
  455. ------------------------------------------------------------------------------
  456.  
  457.                          >>  STYLE OPTIMISING  <<
  458.                             ------------------
  459.  
  460. *S Use a Repeat / Until loop instead of a For / Next loop.
  461.  
  462.    Eg.
  463.       Don't use:
  464.          For n=0 to 10
  465.             [code]
  466.          Next n
  467.  
  468.       Use this instead:
  469.          n=0
  470.          Repeat
  471.             [code]
  472.             n=n+1
  473.          Until n=11 : Rem Must be 1 more than with the original For
  474.  
  475. *S Use "If condition Then code" instead of "If condition : code : End If",
  476.    if the code is short.
  477.  
  478.    Eg.
  479.       Don't use:
  480.          If a=2
  481.             la-de-da
  482.          End If
  483.  
  484.       Use this instead:
  485.          If a=2 Then la-de-da
  486.  
  487. *S Don't use Start(BANK), Screen Width or Screen Height in a routine that
  488.    must be fast. In fact, do the same with any AMOS variable that won't
  489.    change in the loop, including Joy(), JoyUp(), Fire(), etc. if you have
  490.    several references to these in the same "loop".
  491.  
  492.    Eg.
  493.       Don't use:
  494.          For X=1 To 10000
  495.             PE=Peek(Start(10)+X)
  496.          Next X
  497.  
  498.       Use this instead:
  499.          ST=Start(BANK)
  500.          For X=1 To 10000
  501.             PE=Peek(ST+X)
  502.          Next X
  503.  
  504. *M Use the value of expressions, rather than testing what they mean.
  505.  
  506.    Eg.
  507.       Don't use:
  508.          If Jup(1) Then Y=Y-1
  509.          If Jdown(1) Then Y=Y+1
  510.  
  511.       Use this instead:
  512.          Y=Y+Jup(1)-Jdown(1)
  513.  
  514.     Remember: If it is True, -1 is returned, otherwise 0 is returned.
  515.  
  516.     The Turbo (Plus) extension has a useful command called "Texp" which
  517.     returns specified values for True and False. Although it isn't very
  518.     fast, it produces neater code.
  519.  
  520. *M Don't use parameters in Def Fns. As functions are local to procedures,
  521.    any variables used, will be known in the function.
  522.  
  523.    Eg.
  524.       Don't use:
  525.          Def Fn C(x,y)=(x*y*y+x) mod 16
  526.          For x=0 To 10
  527.             For y=0 To 10
  528.                Plot x,y,Fn C(x,y)
  529.             Next y
  530.          Next x
  531.  
  532.       Use this instead:
  533.          Def Fn C=(x*y*y+x) mod 16
  534.          For x=0 To 10
  535.             For y=0 To 10
  536.                Plot x,y,Fn C
  537.             Next y
  538.          Next x
  539.  
  540. *S Use as few brackets in equations and If structures as possible.
  541.  
  542.    Eg.
  543.       Don't use:
  544.          A=(B*C)-(D*E)-(F+G)
  545.          C=(A=2 or B=100)
  546.  
  547.       Use this instead:
  548.          A=B*C-D*E-F-G
  549.          C=A=2 or B=100
  550.  
  551. *M The fastest way to display a map, using a few of the above tips:
  552.  
  553.       ' This is the slow way, that I've seen in multiple sources of games
  554.       For Y=0 To FHV
  555.          For X=0 To FWV
  556.             Paste Icon 16+X*16,16+Y*16,Peek((FPOSY+Y)*FW+Start(BLS1)+FPOSX)+1
  557.          Next X
  558.       Next Y
  559.  
  560.       ' Now my quick version... Note that the start(bank)
  561.       ' is placed in front of the routine.
  562.       ' FHV   = Field Height View
  563.       ' FHV   = Field Width  View
  564.       ' FPOSX = Field POSition X (Where the player is looking in the map)
  565.       ' FPOSY = Field POSition Y
  566.       POS=Start(BLS1)+FPOSX
  567.       For Y=0 To FHV
  568.          MPY=(FPOSY+Y)*FW+POS
  569.          SPY=16+Y*16
  570.          For X=0 To FWV
  571.             ' Using a Turbo Plus extension command
  572.             F Paste Icon 16+X*16,SPY,Peek(MPY+X)+1
  573.          Next X
  574.       Next Y
  575.  
  576. *V Try various ways of writing code to see if you can find the quickest.
  577.    THIS is probably the SINGLE most important TIP for blazing speed.
  578.    You can fine-tune, even convert it to pure Assembler but, it may be MUCH
  579.    slower than another method you have not yet tried!!
  580.  
  581.    Find that faster method!!
  582.  
  583.    Something like this is a nice way to test your improvements:
  584.  
  585.       Timer=0
  586.       For n=0 To 10000
  587.          [unoptimised code]
  588.       Next n
  589.       Print "Time for unoptimised code:";Timer
  590.  
  591.       Timer=0
  592.       For n=0 To 10000
  593.          Optimised code
  594.       Next n
  595.       Print "Time for optimised code:";Timer
  596.  
  597.    Note: This kind of test becomes quite useless if you have a CPU cache -
  598.    The second loop may report to be slower even if it's exactly the same.
  599.    When doing such tests, you should disable the cache if possible.
  600.  
  601.    The key to gaining the absolute BEST speed is to ask yourself WHY is this
  602.    routine so slow? WHAT is slowing it down? The excellent tips above will
  603.    probably provide you with the answer more often than not but the secret
  604.    is you have to know what to look for. Think about what the computer does
  605.    BEHIND THE SCENES.
  606.  
  607.    For example, why are two-dimensional arrays slower than single-dimensional
  608.    arrays? Or, for that matter, why are even one-dimensional arrays slow?
  609.  
  610.    Think of it like this... When you access an element in a single-
  611.    dimensional array the computer has to find the the Base address of the
  612.    array. Then it must find the offset to reference to element you requested.
  613.  
  614.    If you had something like A=MyTable(10) the computer would process this
  615.    along these lines:
  616.  
  617.       1. Find base address of array MyTable.
  618.       2. Calculate offset of element 10 by multiplying 10 X 4 (4 is size of
  619.          integer).
  620.       3. Now add offset to array base to determine the element's memory
  621.          address.
  622.       4. Finally, extract the value held in this memory location.
  623.  
  624.    As you can see that's quite a bit of work (particularly in a loop) just
  625.    to do something that appears so simple on this end.
  626.  
  627.    Now, for a two dimensional array, the computer has twice the work, two
  628.    multiplies to calculate to determine the address of any given two
  629.    dimensional array elements such as A=MyTable(10,10).
  630.  
  631.    Like the Gosub replacing Procedure tip, this is a bit messier but,
  632.    everything has a price so try this:
  633.  
  634.    MyTableBase=VarPtr(MyTable)
  635.    Now instead of accessing the array through AMOS with A=MyTable(10)
  636.    access it directly yourself like this A=Leek(MyTableBase+40). The 40 is
  637.    the offset for element 10, multiplied by the integer size, 4.
  638.  
  639.    Used in a loop this access a one-dimensional array 20% faster than
  640.    the standard AMOS code (ie A=Array(element)) and is 33% faster at
  641.    accessing two-dimensional arrays.
  642.  
  643.    If you don't need long-word size variables, you can substitute Deek
  644.    for Leek for a TINY bit more speed (allowing 25% faster access of
  645.    one-dimensional arrays and 40% faster access of two-dimensional arrays).
  646.  
  647.    Should be better but, AMOS's Peek, Deek and Leek are kind of slow
  648.    themselves!!
  649.  
  650. ------------------------------------------------------------------------------
  651.  
  652. If you find any general purpose tips, please send them to me (at
  653. bwyatt@paston.co.uk) and I'll add them to the list!
  654.  
  655. If you want proof that these tips do work, then take a look at two of my
  656. recent games, Borisball and Knockout 2. Apart from being tremendously good
  657. fun, they demonstrate how the AMOS speed limit can be broken!! Their Aminet
  658. positions are:
  659.    game/demo/BorisBall.lha
  660.    game/2play/KnockOut2.lha
  661.